<!DOCTYPE html>
<html lang="zh-CN">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <script>
    // 用常量 定义的状态,避免后续  硬编码(代码中写死某个值)
    const PENDING = 'pending'
    const FULFILLED = 'fulfilled'
    const REJECTED = 'rejected'

    // 定义类
    class MyPromise {
      // 状态
      state = PENDING
      // 结果 成功/失败
      result = undefined

      // 异步的回调函数 [{onFulfilled,onRejected,resolve,reject}]
      #handlers = []

      // 构造函数,resolve定义,reject定义,执行传入的回调函数
      constructor(executor) { // new MyPromise((resolve,reject)=>{ console.log('立刻执行') })
        const resolve = (result) => {
          // 修改状态+执行保存的回调函数
          this.#changeState(FULFILLED, result)
          this.#runHandlers()
        }
        const reject = (result) => {
          // 修改状态+执行保存的回调函数
          this.#changeState(REJECTED, result)
          this.#runHandlers()
        }
        executor(resolve, reject)
      }

      // 执行回调函数,取出数组中的回调函数,执行到没有为止
      #runHandlers() {
        // 循环执行到数组长度为0为止
        while (this.#handlers.length > 0) {
          const { onFulfilled, onRejected, resolve, reject } = this.#handlers.shift()// unshift
          if (this.state === FULFILLED) { // this.state='fulfilled'
            // 成功 执行 和 then中类似的逻辑
            // 获取结果,根据是否为Promise以及状态调用对应的逻辑
            //  onFulfilled(this.result)
            this.#runPromise(onFulfilled, resolve, reject)
          } else if (this.state === REJECTED) {
            // 失败
            // onRejected(this.result)
            this.#runPromise(onRejected, resolve, reject)
          }
        }
      }

      // 改变状态(pending时才可以修改,保存成功/失败的结果)
      #changeState(state, result) {
        if (this.state !== PENDING) {
          return
        }
        this.state = state
        this.result = result
      }

      // 获取回调函数的结果,根据类型执行不同的逻辑
      #runPromise(callBack, resolve, reject) {
        queueMicrotask(() => {
          try {
            // 调用回调函数 获取执行的结果
            const res = callBack(this.result)
            if (res instanceof MyPromise) {
              // res是Promise对象 then方法
              res.then(resolve, reject)
            } else {
              // 如果是普通的值,直接resolve
              if (this.state === FULFILLED) {
                resolve(res)
              } else if (this.state === REJECTED) {
                reject(res)
              }
            }
          } catch (error) {
            // console.log('catch:', error)
            reject(error)
          }

        })

      }

      // 链式编程 promise对象.then(xxx).then(xxx)
      then(onFulfilled, onRejected) {
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (x) => x // function(x){ return x }
        onRejected = typeof onRejected === 'function' ? onRejected : (x) => { throw x } // function(x){ throw x }

        // 1. 创建一个新的Promise对象 并返回
        const p2 = new MyPromise((resolve, reject) => {
          if (this.state === FULFILLED) {
            this.#runPromise(onFulfilled, resolve, reject)
          } else if (this.state === REJECTED) {
            this.#runPromise(onRejected, resolve, reject)
          } else {
            // pending状态(状态还没改变,回调函数还不能执行)
            // 以对象的形式保存回调函数,成功和失败都保存了
            this.#handlers.push({
              onFulfilled, onRejected, resolve, reject
            })
          }
        })

        return p2
      }

      // 实例方法-catch 本质 then(undefined,onRejected)
      catch(onRejected) {
        return this.then(undefined, onRejected)
      }

      // 实例方法-finally 
      finally(onFinally) {
        // 将传入的回调函数,作为成功/失败的回调函数
        // 成功/失败都会执行
        return this.then(onFinally, onFinally)
      }
    }

    // ------------- 测试 -------------
    const p = new MyPromise((resolve, reject) => {
      resolve('success-1')
    })
    p.then(res => {
      console.log('res:', res)
      return new MyPromise((resolve, reject) => {
        // reject('error-1')
        resolve('success-2')
      })
    }).catch(err => {
      console.log('catch里面的回调函数:', err)
    }).finally(() => {
      console.log('finally-执行啦!!!')
    })
  </script>
</body>

</html>